<template>
  <canvas ref="canvas" class="fit overflow-hidden" @dblclick.stop="handledbClick"></canvas>
  <q-resize-observer @resize="onResize"></q-resize-observer>
</template>

<script setup>
import { onMounted, onUnmounted, ref, reactive } from 'vue'
import * as THREE from 'three'
import { MapControls } from 'three/addons/controls/MapControls.js'
import { MapUtils, initLights } from './MapGlobal/MapUtils'
import { CameraUtils } from './MapGlobal/CameraUtils'
import { mapDataGzip } from './MapGlobal/data_3d'
import { unzip } from 'src/utils/PakoJson'
import { handleMap3dData } from './MapGlobal/utils'
import gsap from 'gsap'

// 解析地图数据
const mapData = JSON.parse(unzip(mapDataGzip.data.baseMapDataGzip))
const mapInfo = handleMap3dData(mapData)

// 组件引用和状态
const canvas = ref(null)
const screenSize = ref({ width: 0, height: 0 })

// 存储全局视图状态（用于双击空白处回归）
const globalView = reactive({
  cameraPos: new THREE.Vector3(),
  controlsTarget: new THREE.Vector3(),
  fov: 40
})

// Three.js核心对象
let scene = null, camera = null, renderer = null, controls = null
let mapDrawer = null, cameraController = null, raycaster = null, mouse = new THREE.Vector2()

// 聚焦配置（重点调整拉远距离参数）
const focusConfig = {
  tiltAngle: Math.PI / 7,        // 倾斜角度（约25.7度，平衡立体与视野）
  distanceFactor: 4.0,           // 拉远因子（值越大离目标越远）
  heightOffset: 1.5,             // 相机高度偏移
  animationDuration: 1,        // 动画时长
  globalViewPadding: 0.15        // 全局视图边距（避免边缘裁剪）
}

const initCanvas = () => {
  scene = new THREE.Scene()

  // 初始化渲染器
  renderer = new THREE.WebGLRenderer({
    canvas: canvas.value,
    antialias: true,
    alpha: true
  })
  renderer.setSize(screenSize.value.width, screenSize.value.height)
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setAnimationLoop(animate)

  // 初始化相机
  camera = new THREE.PerspectiveCamera(
    focusConfig.fov,
    screenSize.value.width / screenSize.value.height,
    0.1,
    1000
  )

  // 初始化控制器
  controls = new MapControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  controls.screenSpacePanning = false
  controls.maxPolarAngle = Math.PI / 2.2  // 保持适度倾斜
  controls.zoomToCursor = true
  controls.enableDoubleClickZoom = false

  // 初始化射线投射器
  raycaster = new THREE.Raycaster()
  raycaster.params.Line.threshold = 0.8

  // 初始化灯光
  initLights(scene)

  // 初始化地图工具
  mapDrawer = new MapUtils(scene, camera, renderer)
  cameraController = new CameraUtils(camera, controls)

  // 绘制地图并适配全局视图
  mapDrawer.drawMap(mapInfo)
  cameraController.fitMapToViewMaximize(mapInfo, focusConfig.globalViewPadding)

  // 保存初始全局视图状态（关键）
  saveGlobalViewState()

  const size = { width: 1700, height: 851 }
  onResize(size)
}

const animate = () => {
  controls.update()
  renderer.render(scene, camera)
}

const onResize = (size) => {
  screenSize.value = size
  if (!camera || !renderer) return
  camera.aspect = size.width / size.height
  camera.updateProjectionMatrix()
  renderer.setSize(size.width, size.height)
}

/**
 * 保存当前视图为全局视图状态
 */
const saveGlobalViewState = () => {
  globalView.cameraPos.copy(camera.position)
  globalView.controlsTarget.copy(controls.target)
  globalView.fov = camera.fov
}

/**
 * 优化的聚焦动画：适当拉远视图
 * @param {THREE.Vector3} targetPoint - 目标点坐标
 */
const animateFocusTo = (targetPoint) => {
  // 1. 计算目标尺寸（动态适配不同大小的目标）
  const targetSize = getTargetSize(targetPoint)

  // 2. 计算拉远后的相机距离（重点优化点1）
  const baseDistance = Math.max(targetSize.x, targetSize.z) * focusConfig.distanceFactor

  // 3. 带倾斜角度的相机位置计算
  const cameraX = targetPoint.x + Math.sin(focusConfig.tiltAngle) * baseDistance
  const cameraZ = targetPoint.z - Math.cos(focusConfig.tiltAngle) * baseDistance
  const cameraY = targetPoint.y + baseDistance * focusConfig.heightOffset  // 提高高度增强立体

  const newCameraPos = new THREE.Vector3(cameraX, cameraY, cameraZ)

  // 4. 目标点微调
  const focusedTarget = new THREE.Vector3(
    targetPoint.x,
    Math.max(targetPoint.y, 0.5),  // 确保目标点有一定高度
    targetPoint.z
  )

  // 执行聚焦动画
  gsap.timeline({ defaults: { ease: "power2.inOut" } })
    .to(camera.position, {
      x: newCameraPos.x,
      y: newCameraPos.y,
      z: newCameraPos.z,
      duration: focusConfig.animationDuration
    })
    .to(controls.target, {
      x: focusedTarget.x,
      y: focusedTarget.y,
      z: focusedTarget.z,
      duration: focusConfig.animationDuration
    }, 0)  // 与相机动画同时开始
    .to(camera, {
      fov: Math.max(35, globalView.fov - 5),  // 略微缩小视野增强聚焦感
      duration: focusConfig.animationDuration,
      onUpdate: () => camera.updateProjectionMatrix()
    }, 0);
}

/**
 * 双击空白处动画回归全局视图（优化点2）
 */
const animateBackToGlobal = () => {
  // 执行回归动画
  gsap.timeline({ defaults: { ease: "power2.inOut" } })
    .to(camera.position, {
      x: globalView.cameraPos.x,
      y: globalView.cameraPos.y,
      z: globalView.cameraPos.z,
      duration: focusConfig.animationDuration
    })
    .to(controls.target, {
      x: globalView.controlsTarget.x,
      y: globalView.controlsTarget.y,
      z: globalView.controlsTarget.z,
      duration: focusConfig.animationDuration
    }, 0)
    .to(camera, {
      fov: globalView.fov,
      duration: focusConfig.animationDuration,
      onUpdate: () => camera.updateProjectionMatrix()
    }, 0);
}

/**
 * 获取目标点的大致尺寸
 * @param {THREE.Vector3} target - 目标点
 * @returns {THREE.Vector3} 尺寸向量
 */
const getTargetSize = () => {
  // 可根据实际模型调整，这里使用通用值
  return new THREE.Vector3(2, 2, 2);
}

/**
 * 处理双击事件
 */
const handledbClick = e => {
  // 计算鼠标归一化坐标
  const rect = renderer.domElement.getBoundingClientRect();
  mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
  mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;

  // 射线检测
  raycaster.setFromCamera(mouse, camera);
  const points = mapDrawer.getPoints();
  const lines = mapDrawer.getLines();
  const intersects = raycaster.intersectObjects([...points, ...lines], true);

  if (intersects.length > 0) {
    // 点击到对象：聚焦到该点
    const targetPoint = intersects[0].point;
    animateFocusTo(targetPoint);
  } else {
    // 双击空白处：动画回归全局视图
    animateBackToGlobal();
  }
}

// 组件生命周期
onMounted(initCanvas)
onUnmounted(() => {
  if (renderer) {
    renderer.setAnimationLoop(null)
  }
  if (mapDrawer) {
    mapDrawer.clearMap()
  }
})
</script>